/**
 * Copyright 2010-present Facebook
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.android;

import android.app.AlertDialog.Builder;
import android.content.Context;
import android.os.Bundle;
import com.facebook.internal.Utility;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.*;
import java.net.*;

/**
 * Utility class supporting the Facebook Object.
 * <p/>
 * THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
 * <p/>
 * All public members of this class are intentionally deprecated.
 * New code should instead use
 * {@link com.facebook.Request}
 * <p/>
 * Adding @Deprecated to this class causes warnings in other deprecated classes
 * that reference this one.  That is the only reason this entire class is not
 * deprecated.
 *
 * @devDocDeprecated
 */
public final class Util
{

  private final static String UTF8 = "UTF-8";

  /**
   * Generate the multi-part post body providing the parameters and boundary
   * string
   *
   * @param parameters the parameters need to be posted
   * @param boundary   the random string as boundary
   * @return a string of the post body
   */
  @Deprecated
  public static String encodePostBody(Bundle parameters, String boundary)
  {
    if (parameters == null) return "";
    StringBuilder sb = new StringBuilder();

    for (String key : parameters.keySet())
    {
      Object parameter = parameters.get(key);
      if (!(parameter instanceof String))
      {
        continue;
      }

      sb.append("Content-Disposition: form-data; name=\"" + key +
          "\"\r\n\r\n" + (String) parameter);
      sb.append("\r\n" + "--" + boundary + "\r\n");
    }

    return sb.toString();
  }

  @Deprecated
  public static String encodeUrl(Bundle parameters)
  {
    if (parameters == null)
    {
      return "";
    }

    StringBuilder sb = new StringBuilder();
    boolean first = true;
    for (String key : parameters.keySet())
    {
      Object parameter = parameters.get(key);
      if (!(parameter instanceof String))
      {
        continue;
      }

      if (first) first = false;
      else sb.append("&");
      sb.append(URLEncoder.encode(key) + "=" +
          URLEncoder.encode(parameters.getString(key)));
    }
    return sb.toString();
  }

  @Deprecated
  public static Bundle decodeUrl(String s)
  {
    Bundle params = new Bundle();
    if (s != null)
    {
      String array[] = s.split("&");
      for (String parameter : array)
      {
        String v[] = parameter.split("=");

        try
        {
          if (v.length == 2)
          {
            params.putString(URLDecoder.decode(v[0], UTF8),
                URLDecoder.decode(v[1], UTF8));
          }
          else
            if (v.length == 1)
            {
              params.putString(URLDecoder.decode(v[0], UTF8), "");
            }
        }
        catch (UnsupportedEncodingException e)
        {
          // shouldn't happen
        }
      }
    }
    return params;
  }

  /**
   * Parse a URL query and fragment parameters into a key-value bundle.
   *
   * @param url the URL to parse
   * @return a dictionary bundle of keys and values
   */
  @Deprecated
  public static Bundle parseUrl(String url)
  {
    // hack to prevent MalformedURLException
    url = url.replace("fbconnect", "http");
    try
    {
      URL u = new URL(url);
      Bundle b = decodeUrl(u.getQuery());
      b.putAll(decodeUrl(u.getRef()));
      return b;
    }
    catch (MalformedURLException e)
    {
      return new Bundle();
    }
  }


  /**
   * Connect to an HTTP URL and return the response as a string.
   * <p/>
   * Note that the HTTP method override is used on non-GET requests. (i.e.
   * requests are made as "POST" with method specified in the body).
   *
   * @param url    - the resource to open: must be a welformed URL
   * @param method - the HTTP method to use ("GET", "POST", etc.)
   * @param params - the query parameter for the URL (e.g. access_token=foo)
   * @return the URL contents as a String
   * @throws MalformedURLException - if the URL format is invalid
   * @throws IOException           - if a network problem occurs
   */
  @Deprecated
  public static String openUrl(String url, String method, Bundle params)
      throws MalformedURLException, IOException
  {
    // random string as boundary for multi-part http post
    String strBoundary = "3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f";
    String endLine = "\r\n";

    OutputStream os;

    if (method.equals("GET"))
    {
      url = url + "?" + encodeUrl(params);
    }
    Utility.logd("Facebook-Util", method + " URL: " + url);
    HttpURLConnection conn =
        (HttpURLConnection) new URL(url).openConnection();
    conn.setRequestProperty("User-Agent", System.getProperties().
        getProperty("http.agent") + " FacebookAndroidSDK");
    if (!method.equals("GET"))
    {
      Bundle dataparams = new Bundle();
      for (String key : params.keySet())
      {
        Object parameter = params.get(key);
        if (parameter instanceof byte[])
        {
          dataparams.putByteArray(key, (byte[]) parameter);
        }
      }

      // use method override
      if (!params.containsKey("method"))
      {
        params.putString("method", method);
      }

      if (params.containsKey("access_token"))
      {
        String decoded_token =
            URLDecoder.decode(params.getString("access_token"));
        params.putString("access_token", decoded_token);
      }

      conn.setRequestMethod("POST");
      conn.setRequestProperty(
          "Content-Type",
          "multipart/form-data;boundary=" + strBoundary);
      conn.setDoOutput(true);
      conn.setDoInput(true);
      conn.setRequestProperty("Connection", "Keep-Alive");
      conn.connect();

      os = new BufferedOutputStream(conn.getOutputStream());

      try
      {
        os.write(("--" + strBoundary + endLine).getBytes());
        os.write((encodePostBody(params, strBoundary)).getBytes());
        os.write((endLine + "--" + strBoundary + endLine).getBytes());

        if (!dataparams.isEmpty())
        {

          for (String key : dataparams.keySet())
          {
            os.write(("Content-Disposition: form-data; filename=\"" + key + "\"" + endLine).getBytes());
            os.write(("Content-Type: content/unknown" + endLine + endLine).getBytes());
            os.write(dataparams.getByteArray(key));
            os.write((endLine + "--" + strBoundary + endLine).getBytes());

          }
        }
        os.flush();
      }
      finally
      {
        os.close();
      }
    }

    String response = "";
    try
    {
      response = read(conn.getInputStream());
    }
    catch (FileNotFoundException e)
    {
      // Error Stream contains JSON that we can parse to a FB error
      response = read(conn.getErrorStream());
    }
    return response;
  }

  @Deprecated
  private static String read(InputStream in) throws IOException
  {
    StringBuilder sb = new StringBuilder();
    BufferedReader r = new BufferedReader(new InputStreamReader(in), 1000);
    for (String line = r.readLine(); line != null; line = r.readLine())
    {
      sb.append(line);
    }
    in.close();
    return sb.toString();
  }

  /**
   * Parse a server response into a JSON Object. This is a basic
   * implementation using org.json.JSONObject representation. More
   * sophisticated applications may wish to do their own parsing.
   * <p/>
   * The parsed JSON is checked for a variety of error fields and
   * a FacebookException is thrown if an error condition is set,
   * populated with the error message and error type or code if
   * available.
   *
   * @param response - string representation of the response
   * @return the response as a JSON Object
   * @throws JSONException - if the response is not valid JSON
   * @throws FacebookError - if an error condition is set
   */
  @Deprecated
  public static JSONObject parseJson(String response)
      throws JSONException, FacebookError
  {
    // Edge case: when sending a POST request to /[post_id]/likes
    // the return value is 'true' or 'false'. Unfortunately
    // these values cause the JSONObject constructor to throw
    // an exception.
    if (response.equals("false"))
    {
      throw new FacebookError("request failed");
    }
    if (response.equals("true"))
    {
      response = "{value : true}";
    }
    JSONObject json = new JSONObject(response);

    // errors set by the server are not consistent
    // they depend on the method and endpoint
    if (json.has("error"))
    {
      JSONObject error = json.getJSONObject("error");
      throw new FacebookError(
          error.getString("message"), error.getString("type"), 0);
    }
    if (json.has("error_code") && json.has("error_msg"))
    {
      throw new FacebookError(json.getString("error_msg"), "",
          Integer.parseInt(json.getString("error_code")));
    }
    if (json.has("error_code"))
    {
      throw new FacebookError("request failed", "",
          Integer.parseInt(json.getString("error_code")));
    }
    if (json.has("error_msg"))
    {
      throw new FacebookError(json.getString("error_msg"));
    }
    if (json.has("error_reason"))
    {
      throw new FacebookError(json.getString("error_reason"));
    }
    return json;
  }

  /**
   * Display a simple alert dialog with the given text and title.
   *
   * @param context Android context in which the dialog should be displayed
   * @param title   Alert dialog title
   * @param text    Alert dialog message
   */
  @Deprecated
  public static void showAlert(Context context, String title, String text)
  {
    Builder alertBuilder = new Builder(context);
    alertBuilder.setTitle(title);
    alertBuilder.setMessage(text);
    alertBuilder.create().show();
  }
}
